Skip to content

feat(proxy-rewrite): support multiple same-name headers in headers.add/set#13597

Open
AlinsRan wants to merge 4 commits into
apache:masterfrom
AlinsRan:feat/13163-multi-value-headers
Open

feat(proxy-rewrite): support multiple same-name headers in headers.add/set#13597
AlinsRan wants to merge 4 commits into
apache:masterfrom
AlinsRan:feat/13163-multi-value-headers

Conversation

@AlinsRan

@AlinsRan AlinsRan commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Allow headers.set and headers.add values in proxy-rewrite to be an array, producing multiple headers with the same name:

"proxy-rewrite": {
  "headers": {
    "set": { "X-Api-Version": ["v1", "v2"] }
  }
}
  • set with an array replaces any incoming header with the listed values.
  • add with an array appends the listed values to the existing ones.
  • remove is unchanged (it already takes an array of names).

Why

proxy-rewrite models headers.add/set as a JSON object (map), which structurally cannot express same-name multi-value headers — a JSON object can't hold two values for one key. This is a real gap vs. other gateways that model header transforms as an ordered list of (name, value) pairs (Kong request-transformer append.headers, Envoy request_headers_to_add, HAProxy add-header). The array form closes it. Genuine use cases: gRPC metadata (repeated keys → repeated HTTP/2 headers) and backends/auth proxies that read repeated custom headers.

Fully backward compatible: existing string/number values keep working; the array is an additive schema option. Each element still supports NGINX variables and regex_uri captures.

This also gives the clean answer to the remove + add ordering confusion in #11587 — multi-value is a data-model feature, not an operation-order one.

Which issue(s) this PR fixes

Closes #11587
Closes #13163

Changes

  • apisix/plugins/proxy-rewrite.lua: schema allows array<string|number> for add/set values; apply loop resolves each element (set → one set_header with a table; add → one add_header per element).
  • t/plugin/proxy-rewrite.t: tests for set (replace with multiple) and add (append multiple); t/lib/server.lua: echo helper joining same-name headers.
  • docs/{en,zh}/.../proxy-rewrite.md: attribute docs + example.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (if not, please discuss on the APISIX mailing list first)

AlinsRan added 2 commits June 23, 2026 12:02
Allow headers.set and headers.add values to be an array (e.g.
"foo": ["v1", "v2"]), producing multiple headers with the same name:
- set replaces any incoming header with the listed values;
- add appends the listed values to the existing ones.

The map-based headers schema previously could not express same-name
multi-value headers (a JSON object can't hold two values for one key);
the array form closes that gap. remove is unchanged (it already takes an
array of names). Backward compatible: string/number values keep working.

Closes apache#13163
The echo helper joined same-name headers with ", ", so the assertions
("x-multi: val1, val2") could not tell a genuine multi-header result apart
from a single comma-joined value. Emit one line per header occurrence and
assert the repeated "x-multi: ..." lines, so the test actually proves that
set/add with an array produces multiple headers with the same name.
@AlinsRan AlinsRan changed the title feat(proxy-rewrite): support multiple values for set/add headers feat(proxy-rewrite): support multiple same-name headers in headers.add/set Jun 24, 2026
AlinsRan added 2 commits June 26, 2026 09:01
…y comment

- t/plugin/proxy-rewrite.t: add cases where each array element of
  headers.set/add mixes $1 (regex_uri capture) and $http_x_src (nginx
  variable), since the docs promise per-element variable/capture support.
- proxy-rewrite.lua: clarify the add_header guard comment — it filters nil
  (which throws); an empty string is a valid value and is intentionally kept.
@AlinsRan AlinsRan marked this pull request as ready for review June 26, 2026 07:32
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Jun 26, 2026
Comment thread apisix/plugins/proxy-rewrite.lua

@membphis membphis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@shreemaan-abhishek shreemaan-abhishek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both "multiple values for same header" and "same header with multiple values - (comma separated or not)" are the same.

So even without this PR, set can already set multiple values for a header by specifying comma separated values.

The add feature in this PR is nice to have.

@AlinsRan

AlinsRan commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

I think both "multiple values for same header" and "same header with multiple values - (comma separated or not)" are the same.

I don't think they're fully interchangeable. A headers.set object can't express same-name multi-value headers at all (a JSON object can't hold two values for one key), and emitting repeated lines is a first-class, intentional behavior — both HTTP and the underlying OpenResty support it (ngx.req.set_header(name, {...})). The array closes that gap.

Real headers that rely on multiple same-name fields rather than a comma list:

  • Set-Cookie — non-combinable by spec.
  • Cookie over HTTP/2 — carried as multiple field lines, recombined with "; " not ",".
  • gRPC metadata — a multimap; in practice grpc-go/grpc-java neither fold duplicates nor split on commas, so ["a","b"] reaches the app as two entries and "a,b" as one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: proxy-rewrite header priority wrong bug: proxy-rewrite silently drops duplicate keys in headers.add due to JSON object limitation

4 participants